package cn.dream.ebag.MessengerLib;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;

import java.lang.ref.WeakReference;

import cn.dream.ebag.MessengerLib.constants.MMessageWhat;
import cn.dream.ebag.MessengerLib.constants.MSAction;
import cn.dream.ebag.MessengerLib.listeners.OnMCListener;
import cn.dream.ebag.MessengerLib.util.ServiceUtil;

import static cn.dream.ebag.MessengerLib.constants.MMParam.PARAM_DATA;


/**
 * @ProjectName: MessengerServer
 * @Package: cn.dream.mgclient
 * @Author: qktang
 * @CreateDate: 2021/1/11 10:14
 * @Description: 描述
 */
public abstract class MGClient implements OnMCListener {
    private static final String TAG = "MGClient";

    private WeakReference<Context> mWeakContext;
    private Messenger mServerMessenger, mSelfMessenger;
    private boolean hasBindService = false;
    private MCHandler mHandler;
    private int curHeartbeatCount = 0;
    private static final int MAX_HEARTBEAT_COUNT = 5;
    private static final int MSG_WHAT_LOCAL_HEARTBEAT = 110;
    private static final int MSG_WHAT_LOCAL_CONNECT_TIMEOUT = 111;
    private String lastServerPkg;
    private boolean needHeartbeat = false;
    private boolean isAppExit = false;
    private boolean isBindingService = false;
    private String connectTag;

    private static class MCHandler extends WeakReferenceHandler<MGClient> {

        public MCHandler(MGClient weakReference) {
            super(weakReference);
        }

        @Override
        public void handleMessage(MGClient instance, Message msg) {
            if (instance == null) {
                return;
            }
            Bundle data = msg.getData();
            switch (msg.what) {
                case MMessageWhat.WHAT_MTC_CONNECT:
                    Log.e(TAG, "handleMessage: WHAT_MTC_CONNECT");
                    if (data != null) {
                        instance.onConnect(data.getString(PARAM_DATA));
                    }

                    break;
                case MMessageWhat.WHAT_MTC_HEART:
                    Log.e(TAG, "handleMessage: WHAT_MTC_HEART");
                    instance.curHeartbeatCount = 0;
                    break;
                case MMessageWhat.WHAT_MTC_JSON:
                    Log.e(TAG, "handleMessage: WHAT_MTC_JSON");
                    if (data != null) {
                        Log.d(TAG, "handleMessage: " + data.getString(PARAM_DATA));
                        instance.onJsonMsg(data.getString(PARAM_DATA));
                    }
                    break;
                case MMessageWhat.WHAT_MTC_ERROR:
                    Log.e(TAG, "handleMessage: WHAT_MTC_ERROR");
                    if (data != null) {
                        instance.onErrorMsg(data.getString(PARAM_DATA));
                    }
                    break;
                case MMessageWhat.WHAT_MTC_CLOSE:
                    Log.e(TAG, "handleMessage: WHAT_MTC_CLOSE");
                    instance.onCloseMsg();
                    break;
                case MSG_WHAT_LOCAL_HEARTBEAT:
                    Log.e(TAG, "handleMessage: WHAT_LOCAL_HEARTBEAT");
                    instance.sendHeartbeat2Server();
                    break;
                case MSG_WHAT_LOCAL_CONNECT_TIMEOUT:
                    Log.e(TAG, "handleMessage: MSG_WHAT_LOCAL_CONNECT_TIMEOUT");
                    instance.isBindingService = false;
                    instance.startConnect(instance.lastServerPkg, instance.connectTag);
                    break;
                default:break;
            }
        }
    }

    public MGClient(Context mContext) {
        mWeakContext = new WeakReference<>(mContext.getApplicationContext());
        mHandler = new MCHandler(this);
        mSelfMessenger = new Messenger(mHandler);
    }

    public void startConnect(@NonNull String serverPkg, String tag) {
        if (mWeakContext == null || mWeakContext.get() == null) {
            return;
        }
        if (isBindingService) {
            Log.e(TAG, "startConnect: is Binding ");
            return;
        }
        if (!ServiceUtil.isServiceExitByAction(mWeakContext.get(), MSAction.ACTION_CONNECT)) {
            onErrorMsg("未找到服务，请确认是否已安装服务apk");
            return;
        }
        connectTag = tag;
        isBindingService = true;
        mHandler.sendEmptyMessageDelayed(MSG_WHAT_LOCAL_CONNECT_TIMEOUT, 3 * 1000);
        lastServerPkg = serverPkg;
        //绑定服务
        Intent intent = new Intent();
        intent.setPackage(serverPkg);
        intent.setAction(MSAction.ACTION_CONNECT);
        hasBindService = mWeakContext.get().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
        Log.d(TAG, "startConnect: hasBindService=" + hasBindService);
    }

    public void setHeartbeatEnable(boolean enable) {
        needHeartbeat = enable;
        if (hasBindService) {
            mHandler.obtainMessage(MSG_WHAT_LOCAL_HEARTBEAT).sendToTarget();
        }
    }

    private void sendHeartbeat2Server() {
        if (!hasBindService) {
            Log.e(TAG, "sendHeartbeat2Server: did't bind service");
            return;
        }
        if (curHeartbeatCount >= MAX_HEARTBEAT_COUNT) {
            Log.d(TAG, "sendHeartbeat2Server: can't receive heartbeat msg");
            closeConnect();
            startConnect(lastServerPkg,connectTag);
        }
        Bundle bundle = new Bundle();
        bundle.putString(PARAM_DATA, "heartbeat");
        sendMsg2Server(bundle, MMessageWhat.WHAT_CTM_HEART);
        mHandler.sendEmptyMessageDelayed(MSG_WHAT_LOCAL_HEARTBEAT, 10 * 1000);
    }

    private void sendConnectRequest2Server() {
        if (TextUtils.isEmpty(connectTag)) {
            connectTag = MGClient.TAG + System.currentTimeMillis();
        }
        Bundle bundle = new Bundle();
        bundle.putString(PARAM_DATA, connectTag);
        sendMsg2Server(bundle, MMessageWhat.WHAT_CTM_CONNECT);
    }

    private void sendMsg2Server(Bundle bundle, int what) {
        if (mServerMessenger == null || mSelfMessenger == null) {
            throw new IllegalStateException("You don't have bind a service");
        }
        if (!hasBindService) {
            throw new IllegalStateException("You don't have bind a service");
        }
        Message message = Message.obtain(null, what);
        message.replyTo = mSelfMessenger;
        message.setData(bundle);
        try {
            Log.d(TAG, "sendMsg2SmartService: message=" + message.what);
            mServerMessenger.send(message);
        } catch (RemoteException e) {
            e.printStackTrace();
            onErrorMsg(e.getMessage());
            hasBindService = false;
        }
    }

    public boolean isConnected() {
        Log.d(TAG, "isConnected: " + hasBindService);
        return hasBindService;
    }

    private void closeConnect() {
        if (mHandler != null) {
            mHandler.removeCallbacksAndMessages(null);
        }
        if (mWeakContext != null && mWeakContext.get() != null) {
            try {
                mWeakContext.get().unbindService(serviceConnection);
            } catch (Exception e) {
                onErrorMsg(e.getMessage());
            }
        }
    }

    public void closeConnectByExit() {
        Log.d(TAG, "closeConnectByExit: ");
        sendCloseMsg();
        closeConnect();
        isAppExit = true;
    }


    private void sendCloseMsg(){
        Bundle bundle = new Bundle();
        bundle.putString(PARAM_DATA, connectTag);
        sendMsg2Server(bundle, MMessageWhat.WHAT_CTM_CLOSE);
    }

    public void sendJsonMsg(String jsonMsg) {
        Bundle bundle = new Bundle();
        bundle.putString(PARAM_DATA, jsonMsg);
        sendMsg2Server(bundle, MMessageWhat.WHAT_CTM_JSON);
    }

    public void sendErrorMsg(String errorMsg) {
        Bundle bundle = new Bundle();
        bundle.putString(PARAM_DATA, errorMsg);
        sendMsg2Server(bundle, MMessageWhat.WHAT_CTM_ERROR);
    }


    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            hasBindService = true;
            isBindingService = false;
            mHandler.removeMessages(MSG_WHAT_LOCAL_CONNECT_TIMEOUT);
            mServerMessenger = new Messenger(service);
            sendConnectRequest2Server();
            if (needHeartbeat && !mHandler.hasMessages(MSG_WHAT_LOCAL_HEARTBEAT)) {
                mHandler.obtainMessage(MSG_WHAT_LOCAL_HEARTBEAT).sendToTarget();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected: ");
            hasBindService = false;
            if (!isAppExit) {
                startConnect(lastServerPkg,connectTag);
                return;
            }
            onDisConnect();
        }
    };
}
